package com.pingan.common.core.property;

import java.lang.reflect.Field;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
import org.springframework.util.ReflectionUtils;

/**
 * @author xxu
 * @date 2010-12-22
 * @version $id$
 */
public class PropertiesAnnotationBeanPostProcessor extends
		PropertyPlaceholderConfigurer implements BeanPostProcessor,
		InitializingBean, PropertiesGet {

	private static transient Log logger = LogFactory
			.getLog(PropertiesAnnotationBeanPostProcessor.class);

	private java.util.Properties pros;

	private final Map<Class<?>, PropertiesInjectionMetadata> injectionMetadataCache = new ConcurrentHashMap<Class<?>, PropertiesInjectionMetadata>();

	public Object postProcessAfterInitialization(Object bean, String beanName)
			throws BeansException {
		return bean;
	}

	private PropertiesInjectionMetadata findPropertiesMetadata(
			final Class<?> clazz) {
		// Quick check on the concurrent map first, with minimal locking.
		PropertiesInjectionMetadata metadata = this.injectionMetadataCache
				.get(clazz);
		if (metadata == null) {
			synchronized (this.injectionMetadataCache) {
				metadata = this.injectionMetadataCache.get(clazz);
				if (metadata == null) {
					final PropertiesInjectionMetadata newMetadata = new PropertiesInjectionMetadata();
					// 这里用了一个回调函数FieldCallback,循环的把clazz类的用了@Properties注解的成员变量加到newMetadata中
					ReflectionUtils.doWithFields(clazz,
							new ReflectionUtils.FieldCallback() {
								public void doWith(Field field) {
									newMetadata.addInjectedField(field);
								}
							});

					if (!newMetadata.getInjectedFields().isEmpty()) {
						metadata = newMetadata;
						this.injectionMetadataCache.put(clazz, metadata);
					}
				}
			}
		}
		return metadata;
	}

	public Object postProcessBeforeInitialization(Object bean, String beanName)
			throws BeansException {
		if (logger.isDebugEnabled())
			logger.debug("prepare inject properties for " + bean.getClass());
		// 获得哪些bean用了Properties注解
		PropertiesInjectionMetadata metadata = findPropertiesMetadata(bean
				.getClass());
		try {
			if (metadata != null)
				metadata.injectFields(this.pros, bean, beanName);
		} catch (Throwable ex) {
			throw new BeanCreationException(beanName,
					"Properties of fields failed", ex);
		}
		return bean;
	}

	public void afterPropertiesSet() throws Exception {
		/*
		 * 把在spring-bean.xml中配置的.properties文件中的值读入内存<br> key为=号前的，value为=号后的
		 */
		pros = mergeProperties();
	}

	public String get(String key) {
		return this.pros.getProperty(key);
	}

}
